﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Keepo.Helpers
{
    public class TemplateMappingHelper
    {
        static public void searchExample()
        {
            byte[] byteArray = { 
                                   0x50, 0x30, 
                                   0x70, 0x48, 0x59,
                                   0x70, 0x48, 
                                   0x70, 0x48, 0x59, 0x73,
                                   0x1, 0x2, 0x3, 0x4,
                                   0x5, 0x6, 0x7, 0x8
                               };

            changeDPI(byteArray, 300);
        }


        static public byte[] createDpmByteArray(float DPI)
        {
            uint DPM = (uint)(DPI / 2.54f * 100.0f);

            byte[] dpmAsByteArray = new byte[4];
            dpmAsByteArray[0] = (byte)((DPM & 0xFF000000) >> 24);
            dpmAsByteArray[1] = (byte)((DPM & 0x00FF0000) >> 16);
            dpmAsByteArray[2] = (byte)((DPM & 0x0000FF00) >> 8);
            dpmAsByteArray[3] = (byte)(DPM & 0x000000FF);

            return dpmAsByteArray;
        }

        static public byte[] createPhysByteArray(float DPI)
        {
            byte[] dpmAsByteArray = createDpmByteArray(DPI);
            byte[] sizeByteArray = { 0x00, 0x00, 0x00, 0x09 };
            byte[] physMarker = { 0x70, 0x48, 0x59, 0x73 };
            byte[] crcByteArray = { 0x1, 0x00, 0x00, 0x00, 0x00 };

            return sizeByteArray.Concat(physMarker)
                .Concat(dpmAsByteArray)
                .Concat(dpmAsByteArray)
                .Concat(crcByteArray)
                .ToArray();
        }

        // Changes the DPI of PNG image given as byteArray
        static public byte[] changeDPI(byte[] byteArray, float DPI)
        {
            byte[] dpmAsByteArray = createDpmByteArray(DPI);

            int idx = searchPhysMarker(byteArray);
            
            // If exist replace
            if (idx != -1)
            {
                replaceSubArray(byteArray, dpmAsByteArray, idx + 4);
                replaceSubArray(byteArray, dpmAsByteArray, idx + 8);
                return byteArray;
            }
            else // Else inject
            {
                idx = searchIhdrMarker(byteArray);
                
                // Read size before, (Little Endian ?)
                int ihdrSize = readIntFromByteArray(byteArray, idx - 4); 
                
                // jump + 8 (include Marker and CRC)
                int newPos = ihdrSize + idx + 8;

                byte[] physByteArray = createPhysByteArray(DPI);

                // Inject
                return byteArray.Take(newPos)
                    .Concat(physByteArray)
                    .Concat(byteArray.Skip(newPos))
                    .ToArray();
                
            }

        }


        static public int readIntFromByteArray(byte[] src, int start)
        {
            byte[] subArray = { 1, 2, 3, 4 };
            for (int idx = 0; idx < 4; idx++)
            {
                subArray[4 - idx - 1] = src[start + idx];
            }

            if (BitConverter.IsLittleEndian)
            {
                subArray.Reverse();
            }

            return BitConverter.ToInt32(subArray, 0);
        }

        static public void replaceSubArray(byte[] dst, byte[] src, int idx)
        {
            for (int offset = 0; offset < src.Length; offset++)
            {
                dst[idx + offset] = src[offset];
            }
        }

        static public int searchMarker(byte[] marker, byte[] byteArray)
        {
            for (int i = 0; i < byteArray.Length; i++)
            {
                int idx = 0;
                while (idx < byteArray.Length &&
                    marker[idx] == byteArray[i + idx])
                {
                    idx++;
                    if (idx == marker.Length)
                    {
                        return i;
                    }
                }
            }

            return -1;
        }

        static public int searchIhdrMarker(byte[] byteArray)
        {
            byte[] ihdrMarker = { 0x49, 0x48, 0x44, 0x52 };
            return searchMarker(ihdrMarker, byteArray);
        }

        static public int searchPhysMarker(byte[] byteArray)
        {
            byte[] physMarker = { 0x70, 0x48, 0x59, 0x73 };
            return searchMarker(physMarker, byteArray);
        }
    }
}